package com.zkjl.protect_business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.zkjl.protect_business.dto.PageBaseDTO;
import com.zkjl.protect_business.entity.Business;
import com.zkjl.protect_business.entity.BusinessField;
import com.zkjl.protect_business.entity.BusinessFieldExtend;
import com.zkjl.protect_business.entity.PBFile;
import com.zkjl.protect_business.repository.BusinessRepository;
import com.zkjl.protect_business.repository.PBFileRepository;
import com.zkjl.protect_business.service.BusinessService;
import com.zkjl.protect_business.service.RedisService;
import com.zkjl.protect_business.service.SiteSearchService;
import com.zkjl.protect_business.utils.QueryUtil;
import com.zkjl.protect_business.vo.SearchResultVO;
import com.zkjl.protect_business.vo.SiteSearchVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;

import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

import static com.zkjl.protect_business.common.Consts.INDEX_PREFIX;
import static org.elasticsearch.search.sort.SortOrder.DESC;

@Service
public class SiteSearchServiceImpl implements SiteSearchService {


    @Autowired
    BusinessService businessService;
    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    PBFileRepository pbFileRepository;
    @Autowired
    RedisService redisService;
    @Autowired
    BusinessRepository businessRepository;


    @Override
    public List<SiteSearchVO> search(String keyword) {

        List<Business> list = businessService.list();

        List<SiteSearchVO> collect = list.stream().filter(business -> {

            boolean notBlank = StringUtils.isNotBlank(business.getPid());
            boolean indexExists = false;
            if (notBlank) {
                String pname = business.getPname();
                String name = business.getName();
                String pinyin = PinyinUtil.getPinyin(name, "");
                String ppinyin = PinyinUtil.getPinyin(pname, "");
                indexExists = elasticsearchRestTemplate.indexExists(INDEX_PREFIX + ppinyin + "_" + pinyin);
            }

            return indexExists && notBlank;

        }).map(business -> {

            String name = business.getName();
            String pname = business.getPname();
            IndexCoordinates indexCoordinates = QueryUtil.createIndex(name, pname);
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            BoolQueryBuilder qb = QueryBuilders.boolQuery();

            //全局关键词
            if (StringUtils.isNotEmpty(keyword)) {
                //精确匹配  https://blog.csdn.net/hereiskxm/article/details/47979013
                MultiMatchQueryBuilder type = new MultiMatchQueryBuilder(keyword).type(MultiMatchQueryBuilder.Type.PHRASE).slop(0);
                qb.filter(type);
            }

            queryBuilder.withQuery(qb);
            long count = elasticsearchRestTemplate.count(queryBuilder.build(), indexCoordinates);

            SiteSearchVO siteSearchVO = new SiteSearchVO();
            siteSearchVO.setName(name);
            siteSearchVO.setNum(count);
            siteSearchVO.setId(business.getId());

            return siteSearchVO;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public PageImpl<Map<String, Object>> search(PageBaseDTO pageBaseDTO, String businessId) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();

        //全局关键词
        String search = pageBaseDTO.getSearch();
        if (StringUtils.isNotEmpty(search)) {
            //精确匹配  https://blog.csdn.net/hereiskxm/article/details/47979013
            MultiMatchQueryBuilder type = new MultiMatchQueryBuilder(search).type(MultiMatchQueryBuilder.Type.PHRASE).slop(0);
            qb.filter(type);
        }

        //高亮搜索
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.requireFieldMatch(false);  //所有字段高亮
        highlightBuilder.field("*"); // 高亮所有的字段
        //.fragmentSize(8);

        //分页
        Pageable pageable = PageRequest.of(pageBaseDTO.getPageNum() - 1, pageBaseDTO.getPageSize(), Sort.Direction.DESC, "create_time");
        queryBuilder.withPageable(pageable);
        queryBuilder.withQuery(qb);
        queryBuilder.withHighlightBuilder(highlightBuilder);

        IndexCoordinates indexCoordinates = null;
        if (StringUtils.isNotBlank(businessId)) {
            Business business = businessService.get(businessId);
            String pname = business.getPname();
            String name = business.getName();

            indexCoordinates = QueryUtil.createIndex(name, pname);
        }else {
            indexCoordinates = IndexCoordinates.of(INDEX_PREFIX + "*");
        }

        SearchRequest searchRequest = new SearchRequest(indexCoordinates.getIndexName());
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//大多数查询参数要写在searchSourceBuilder里
        sourceBuilder.query(qb);

        sourceBuilder.from((int) pageable.getOffset());
        sourceBuilder.size(pageable.getPageSize());
        sourceBuilder.sort("create_time", DESC);

        sourceBuilder.highlighter(highlightBuilder);


        searchRequest.source(sourceBuilder);

        SearchResponse response = elasticsearchRestTemplate.execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
        SearchHit[] hits = response.getHits().getHits();
        List<Map<String, Object>> collect = Arrays.stream(hits).map(searchHit -> {

            Map<String, Object> content = searchHit.getSourceAsMap();
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();

            //获取索引中文名称
            String index = searchHit.getIndex();
            String name = redisService.getbusinessName(index);
            content.put("businessName", name);
            content.put("hit", highlightFields.toString());
            //获取业务的字段
            Business business = businessRepository.findByName(name);
            if (business != null) {
                List<BusinessField> fields = business.getFields();
                if (CollUtil.isNotEmpty(fields)) {
                    content.put("fieldDescribe", fields);
                }else {
                    List<BusinessFieldExtend> list = addField(name);
                    content.put("fieldDescribe", list);
                }

            }

            return content;

        }).collect(Collectors.toList());

        SearchHits hits1 = response.getHits();
        long value = hits1.getTotalHits().value;
        PageImpl<Map<String, Object>> page = new PageImpl(collect, pageable, value);

        return page;
    }


    /**
     * 处理字段映射
     * @param name
     * @param key
     * @param type
     * @param list
     */
    public void addField(String name, String key, String type,List<BusinessFieldExtend> list) {

        BusinessFieldExtend businessFieldExtend = new BusinessFieldExtend();
        businessFieldExtend.setName(name);
        businessFieldExtend.setKey(key);
        businessFieldExtend.setType(type);
        list.add(businessFieldExtend);

    }

    /**
     * 添加定制模块字段
     * @param businessName
     * @return
     */
    public List<BusinessFieldExtend> addField(String businessName) {
        List<BusinessFieldExtend> list = null;
        if ("政治考核".equals(businessName)) {
            list = addFieldZZKH();
        } else if ("业务培训".equals(businessName)) {
            list = addFieldYWPX();
        } else if ("文件通知".equals(businessName)) {
            list = addFieldWJTZ();
        } else if ("单位信息".equals(businessName)) {
            list = addFieldDWXX();
        } else if ("常用模板".equals(businessName)) {
            list = addFieldCYMB();
        }

        return list;
    }

    /**
     * 政治考核字段
     */
    public List<BusinessFieldExtend> addFieldZZKH() {

        List<BusinessFieldExtend> list = new ArrayList<>();
        addField("名称", "name", "string", list);
        addField("考核类型", "type", "string", list);
        addField("考核说明", "describe", "string", list);
        addField("考核时间", "assessment_time", "date", list);
        addField("考核人数", "int", "string", list);
        addField("创建单位", "create_org_name", "string", list);
        return list;

    }



    /**
     * 业务培训字段
     */
    public List<BusinessFieldExtend> addFieldYWPX() {

        List<BusinessFieldExtend> list = new ArrayList<>();
        addField("培训名称", "name", "string", list);
        addField("内容类型", "type", "string", list);
        addField("培训描述", "describe", "string", list);
        addField("附件", "attachment_ids", "file", list);

        return list;
    }

    /**
     * 文件通知字段
     */
    public List<BusinessFieldExtend> addFieldWJTZ() {

        List<BusinessFieldExtend> list = new ArrayList<>();
        addField("通知名称", "name", "string", list);
        addField("通知单位", "org_ids", "string", list);
        addField("通知内容", "content", "string", list);
        addField("状态", "status", "string", list);
        addField("是否可下载", "isdownload", "string", list);
        addField("通知开始时间", "start_time", "date", list);
        addField("通知结束时间", "end_time", "date", list);
        addField("附件", "attachment_ids", "file", list);
        return list;
    }


    /**
     * 单位信息字段
     */
    public List<BusinessFieldExtend> addFieldDWXX() {

        List<BusinessFieldExtend> list = new ArrayList<>();
        addField("单位名称", "org_name", "string", list);
        addField("编号", "org_num", "string", list);
        addField("驻防地区", "region", "string", list);
        addField("在编人数", "number", "int", list);
        addField("扩展字段", "fields", "json", list);
        return list;

    }

    /**
     * 常用模板字段
     */
    public List<BusinessFieldExtend> addFieldCYMB() {

        List<BusinessFieldExtend> list = new ArrayList<>();
        addField("模板名称", "template_name", "string", list);
        addField("类型", "type", "string", list);
        addField("所属业务", "business", "string", list);
        addField("描述", "describe", "string", list);
        addField("包含字段", "fields", "json", list);
        return list;
    }







}
